package org.pz.handler;

import lombok.extern.slf4j.Slf4j;
import org.pz.constant.MallSecurityConstants;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.FilterInvocation;
import sun.security.util.SecurityConstants;

import java.util.Collection;
import java.util.function.Predicate;

/**
 * 鉴权：决定当前用户是否有权限访问当前资源
 * */
@Slf4j
@Configuration
public class MallAccessDecisionManager implements AccessDecisionManager {

    /**
     * 该方法决定用户能否访问资源，在这里判断是否有访问权限
     * @param authentication 用户的认证信息
     * @param o Http 拦截 holder
     * @param collection 哪些角色可以访问当前资源
     * */
    @Override
    public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {

        log.info("认证权限！");

        //1. 匿名用户处理，这里应该跟直接设置 .anonymous().disable() 一样
        String user = authentication.getName();
        if(authentication.isAuthenticated() && MallSecurityConstants.ANONYMOUS.equals(user)){
            throw new AccessDeniedException("匿名用户不允许访问");
        }

        //2. 所有权限都能访问
        if( collection.stream().anyMatch(ca -> SecurityConstants.ALL_PERMISSION.getName().equals(ca.getAttribute())) ){
            return;
        }

        //3. 如果该用户没有任何角色
        if(authentication.getAuthorities().isEmpty()){
            throw new AccessDeniedException("没有权限");
        }

        //4. 对资源需要的角色进行判断是否可以访问
        for(GrantedAuthority ga : authentication.getAuthorities()){

            //2.2 对比权限
            if( collection.stream().anyMatch(ca -> ca.getAttribute().equals(ga.getAuthority())) ){
                return;
            }
        }

        throw new AccessDeniedException("不足");
    }

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return FilterInvocation.class.isAssignableFrom(aClass);
    }
}
